﻿using Bit.Art.Core.Models;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Bit.Art.Engine
{
    /// <summary>
    /// 存储在内存的深度，只存500条价格信息，以树形结构存储
    /// </summary>
    public class MemoryOrderBookRepo
    {
        public MemoryOrderBookRepo(string market, int depth = 500, int orderCapacity = 100000)
        {
            Market = market;
            BidQueue = new OrderQueueTree(Core.Models.TradeSide.Buy);
            AskQueue = new OrderQueueTree(Core.Models.TradeSide.Sell);
            DepthCapacity = depth;
            Capacity = orderCapacity;
        }

        /// <summary>
        /// 交易对名称，格式为：quote-coin
        /// </summary>
        public string Market { get; private set; }

        /// <summary>
        /// 订单簿里最多能支持的价格区间
        /// </summary>
        public int DepthCapacity { get; private set; }
        
        /// <summary>
        /// 订单簿里最多能支持的订单容量
        /// </summary>
        public int Capacity { get; private set; }

        /// <summary>
        /// 订单簿里当前保存的订单数量
        /// </summary>
        public int Count { get { return BidQueue.OrderCount + AskQueue.OrderCount; } }
        
        /// <summary>
        /// 买单深度
        /// </summary>
        private OrderQueueTree BidQueue { get; set; }

        /// <summary>
        /// 卖单深度
        /// </summary>
        private OrderQueueTree AskQueue { get; set; }

        /// <summary>
        /// 用来在<seealso cref="MemoryOrderBookRepo"/>对象的<seealso cref="Market"/>之间，或者<seealso cref="MemoryOrderBookRepo"/>对象与代表交易对的字符串之间做比较
        /// </summary>
        public class MarketComparer : IComparer, IComparer<MemoryOrderBookRepo>
        {
            /// <summary>
            /// IComparer&lt;MemoryOrderBookRepo&gt;接口的实现
            /// </summary>
            /// <param name="x"></param>
            /// <param name="y"></param>
            /// <returns></returns>
            public int Compare(MemoryOrderBookRepo x, MemoryOrderBookRepo y)
            {
                return ((IComparer)this).Compare((object)x, (object)y);
            }

            /// <summary>
            /// IComparer接口实现
            /// </summary>
            /// <param name="x">可以是MemoryOrderBookRepo实例，也可以是字符串实例。</param>
            /// <param name="y">可以是MemoryOrderBookRepo实例，也可以是字符串实例。</param>
            /// <returns>实例间的Market字段的比较结果</returns>
            int IComparer.Compare(object x, object y)
            {
                if (x == null && y == null) return 0;
                else if (x == null && y != null) return -1;
                else if (x != null && y == null) return 1;
                else if (x is MemoryOrderBookRepo && y is MemoryOrderBookRepo)
                    return string.Compare(((MemoryOrderBookRepo)x).Market, ((MemoryOrderBookRepo)y).Market);
                else if (x is string && y is MemoryOrderBookRepo)
                    return string.Compare((string)x, ((MemoryOrderBookRepo)y).Market);
                else if (x is MemoryOrderBookRepo && y is string)
                    return string.Compare(((MemoryOrderBookRepo)x).Market, (string)y);
                else
                    return 0; // TODO：这一点需要仔细测试和考虑一下，是抛出异常好，还是返回一个随意值更好
            }
        }

        /// <summary>
        /// 往内存订单池中添加一个新订单
        /// </summary>
        /// <param name="order">新的订单</param>
        /// <param name="orderid">订单在内存订单池的订单号。</param>
        /// <param name="matched">所有撮合成功，包括部分撮合的订单</param>
        /// <returns>订单的撮合状态</returns>
        public OrderFillType Push(Order order, out ulong orderid, out Order[] matched)
        {
            if (order == null) throw new ArgumentNullException(nameof(order));
            orderid = 0;
            if (order.OrderType == (int)OrderType.Limit)
            {
                return PushLimit(order, out orderid, out matched);
            }
            else if (order.OrderType == (int)OrderType.Market)
            {
                return PushMarket(order, out orderid, out matched);
            }
            else
            {
                throw new NotSupportedException(string.Format($"订单类型【{order.OrderType}】目前尚不支持！"));
            }
        }

        private OrderFillType PushMarket(Order order, out ulong orderid, out Order[] matched)
        {
            throw new NotImplementedException();
        }

        private OrderFillType PushLimit(Order order, out ulong orderid, out Order[] matched)
        {
            if (!order.Price.HasValue)
                throw new ArgumentException("限价单需要指定订单价格!");
            if (order.Price.Value <= 0)
                throw new ArgumentOutOfRangeException("限价单价格必须大于0！");

            orderid = 0;
            matched = null;
            // TODO: 需要将内存订单匹配结果保存到数据库
            //
            // 1. 如果订单匹配则要将匹配结果写入数据库
            // 2. 需要更新用户的余额
            // 3. 即使没有匹配成功，也需要锁住用户对应的余额
            // 4. 有成交结果需要将Ticks广播到subscribers端
            // 5. 挂单需要广播到subscribers端
            if (order.Side == (int)TradeSide.Buy)
            {
                // TryMatch可能会修改订单的VolumeRemaining字段！
                var oft = AskQueue.TryMatchLimit(order, out matched);

                // 如果有剩余的订单，则将剩余的订单push到队列中！
                if (oft != OrderFillType.Filled)
                {
                    if (Count < Capacity)
                        BidQueue.Push(order, out orderid);
                    else
                        oft = OrderFillType.InsertLater;
                }

                return oft;
            }
            else
            {
                var oft = BidQueue.TryMatchLimit(order, out matched);
                if (oft != OrderFillType.Filled)
                {
                    if (Count < Capacity)
                        AskQueue.Push(order, out orderid);
                    else
                        oft = OrderFillType.InsertLater;
                }
                return oft;
            }
        }

        public OrderBookInfo OrderBook(int limit = 500)
        {
            var obi = new OrderBookInfo()
            {
                Market = Market,
                Asks = AskQueue.OrderBook(limit),
                Bids = BidQueue.OrderBook(limit)
            };

            return obi;
        }

        public bool CancelOrder(uint side, decimal price, ulong orderid, ref Order order)
        {
            if (side == (uint)TradeSide.Buy)
            {
                return BidQueue.CancelOrder(price, orderid, ref order);
            }
            else
            {
                return AskQueue.CancelOrder(price, orderid, ref order);
            }
        }
    }
}
